package com.hunter.lucene.util.search;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

import org.apache.lucene.document.Document;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.Query;

import com.hunter.lucene.util.UserData;
import com.hunter.lucene.util.mapping.SearchMapper;
import com.hunter.lucene.util.search.query.AllWhioutScoreSortingCollector;

/**
 * 
 * @author bastengao
 *
 */
public class CollectAllWithoutScoreSoringQuerySession implements QuerySession {
	private Query query = null;// 查询
	private Filter filter = null;// 过滤
	private SearchMapper searchMapper = null;// 映射
	private SearchWorker worker = null;//
	private int batchSize = 100;//
	private List<Integer> docsId = null;
	private int cursor = 0;// 读取ID的位置(还没有读取过些方位，上次读取位置的下一个)
	private Queue<UserData> userDatas = new LinkedList<UserData>();

	public CollectAllWithoutScoreSoringQuerySession(Query query,
			SearchWorker worker, SearchMapper searchMapper) {
		super();
		this.query = query;
		this.worker = worker;
		this.searchMapper = searchMapper;
	}

	public CollectAllWithoutScoreSoringQuerySession(Query query, Filter filter,
			SearchWorker worker, SearchMapper searchMapper) {
		super();
		this.query = query;
		this.filter = filter;
		this.worker = worker;
		this.searchMapper = searchMapper;
	}

	/**
	 * 执行查询
	 * 
	 * @throws SearchException
	 */
	@Override
	public void execute() throws SearchException {
		AllWhioutScoreSortingCollector collector = new AllWhioutScoreSortingCollector();
		if (filter == null) {
			try {
				worker.excuteQuery(query, collector);
				docsId = collector.docs();
			} catch (IOException e) {
				e.printStackTrace();
				throw new SearchException(e);
			}
		} else {
			try {
				worker.excuteQuery(query, filter, collector);
				docsId = collector.docs();
			} catch (IOException e) {
				e.printStackTrace();
				throw new SearchException(e);
			}
		}
		tryEnsureHasOne();
	}

	@Override
	public boolean hasMore() {
		return userDatas.size() > 0;
	}

	@Override
	public UserData next() throws SearchException {
		UserData userData = userDatas.poll();
		tryEnsureHasOne();
		return userData;
	}

	@Override
	public List<Object> next(int length) throws SearchException {
		tryEnsureHasMore(length);
		List<Object> datas = new ArrayList<Object>(length);
		while (datas.size() < length && userDatas.size() > 0) {
			datas.add(userDatas.poll());
		}
		tryEnsureHasOne();
		return datas;
	}

	@Override
	public void close() {
		query = null;
		worker = null;
	}

	/**
	 * 执行此方法，来尽力去保证还有 一个 UserData
	 * @throws SearchException 
	 */
	private void tryEnsureHasOne() throws SearchException {
		tryEnsureHasMore(1);
	}

	/**
	 * 执行此方法，来尽力去保证至少还有 n 个 UserData,但不保证一定有 n 个 UserData
	 * 
	 * @param size
	 * @throws SearchException
	 */
	private void tryEnsureHasMore(int size) throws SearchException {
		while (userDatas.size() < size && cursor < docsId.size()) {
			Document document = null;
			try {
				// if get document by id that not exists ,which will throw this
				// IOException
				// and this method maybe get a document that is deleted
				// already,lucene recommends to verify use isDeleted(id)
				int index = cursor;
				cursor++;
				int docId = docsId.get(index);

				document = worker.get(docId);

				UserData userData = searchMapper.searchMap(document);
				if (userData != null) {
					userDatas.add(userData);
				}
			} catch (CorruptIndexException e) {
				e.printStackTrace();
				throw new SearchException(e);
			} catch (IOException e) {
				e.printStackTrace();
				throw new SearchException(e);
			}

		}
	}

	/**
	 * 设置进行一次搜索，最多保留的结果数。 搜索他会批量的操作，不是把所有的结果都保留。 不是每次得到结果，都要进行搜索，
	 * 而是进行一次批量搜索，当数据不足是，再进行下一次批量操作。这样子可以提高性能。
	 * 
	 * @param size
	 */
	public void setBatchSize(int size) {
		this.batchSize = size;
	}

	/**
	 * 返回批量搜索时的最大结果数。
	 * 
	 * @return
	 */
	public int getBatchSize() {
		return batchSize;
	}
}
